/*
 * Follow
 */
package com.phonemap.models;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Query.CompositeFilterOperator;
import com.google.appengine.api.datastore.Query.Filter;
import com.google.appengine.api.datastore.Query.FilterOperator;
import com.google.appengine.api.datastore.Query.FilterPredicate;
import com.phonemap.common.AppConstant;
import com.phonemap.common.Util;

/**
 * The Class Follow.
 */
public final class Follow {

	/** The Constant USER_ID_FIELD. */
	public static final String USER_ID_FIELD = "user_id";

	/** The Constant FOLLOW_ID_FIELD. */
	public static final String FOLLOW_ID_FIELD = "follow_id";

	/** The Constant FOLLOWED_TIME_FIELD. */
	public static final String FOLLOWED_TIME_FIELD = "followed_time";

	/** The Constant STATUS_FIELD. */
	public static final String STATUS_FIELD = "status";

	/**
	 * Instantiates a new follow.
	 */
	private Follow() {

	}

	/**
	 * Creates the follow.
	 * 
	 * @param userId
	 *            the user id
	 * @param followId
	 *            the follow id
	 * @return the entity
	 */
	public static Entity createFollow(final long userId, final long followId) {
		Entity user = User.getUser(userId);
		if (user != null) {
			Entity follow = Follow.getFollow(followId, user.getKey());
			if (follow == null) {
				follow = new Entity(AppConstant.ENTITY_FOLLOW, followId,
						user.getKey());
				follow.setProperty(USER_ID_FIELD, userId);
				follow.setProperty(FOLLOW_ID_FIELD, followId);
				follow.setProperty(FOLLOWED_TIME_FIELD, new Date());
				follow.setProperty(STATUS_FIELD, AppConstant.REQUEST_STATUS);
				Util.persistEntity(follow);
			}
		}
		return null;
	}

	/**
	 * Insert follow.
	 * 
	 * @param userId
	 *            the user id
	 * @param followId
	 *            the follow id
	 * @return the entity
	 */
	public static Entity insertFollow(final long userId, final long followId) {
		Entity user = User.getUser(userId);
		if (user != null) {
			Entity follow = Follow.getFollow(followId, user.getKey());
			if (follow == null) {
				follow = new Entity(AppConstant.ENTITY_FOLLOW, followId,
						user.getKey());
				follow.setProperty(USER_ID_FIELD, userId);
				follow.setProperty(FOLLOW_ID_FIELD, followId);
				follow.setProperty(FOLLOWED_TIME_FIELD, new Date());
				follow.setProperty(STATUS_FIELD, AppConstant.REQUEST_STATUS);
				Util.persistEntity(follow);
			}
		}
		return null;
	}

	/**
	 * Update follow.
	 * 
	 * @param userId
	 *            the user id
	 * @param followId
	 *            the follow id
	 * @return the entity
	 */
	public static Entity updateFollow(final long userId, final long followId) {
		Entity user = User.getUser(userId);
		if (user != null) {
			Entity follow = Follow.getFollow(followId, user.getKey());
			if (follow == null) {
				follow = new Entity(AppConstant.ENTITY_FOLLOW, followId,
						user.getKey());
				follow.setProperty(USER_ID_FIELD, userId);
				follow.setProperty(FOLLOW_ID_FIELD, followId);
				follow.setProperty(FOLLOWED_TIME_FIELD, new Date());
				Util.persistEntity(follow);
			}
		}
		return null;
	}

	/**
	 * Confirm follow.
	 * 
	 * @param userId
	 *            the user id
	 * @param followId
	 *            the follow id
	 * @return the entity
	 */
	public static Entity confirmFollow(final long userId, final long followId) {
		Entity user = User.getUser(userId);
		if (user != null) {
			Entity follow = Follow.getFollow(followId, user.getKey());
			if (follow != null) {
				follow.setProperty(STATUS_FIELD, AppConstant.CONFIRMED_STATUS);
				Util.persistEntity(follow);
			}
		}
		return null;
	}

	/**
	 * Disconnect follow.
	 * 
	 * @param userId
	 *            the user id
	 * @param followId
	 *            the follow id
	 * @return the entity
	 */
	public static Entity disconnectFollow(final long userId, final long followId) {
		Entity user = User.getUser(userId);
		if (user != null) {
			Entity follow = Follow.getFollow(followId, user.getKey());
			if (follow != null) {
				follow.setProperty(STATUS_FIELD,
						AppConstant.DISCONNECTED_STATUS);
				Util.persistEntity(follow);
			}
		}
		return null;
	}

	/**
	 * Gets the follow.
	 * 
	 * @param followId
	 *            the follow id
	 * @param parentKey
	 *            the parent key
	 * @return the follow
	 */
	public static Entity getFollow(final long followId, final Key parentKey) {
		Key key = KeyFactory.createKey(parentKey, AppConstant.ENTITY_FOLLOW,
				followId);
		Entity entity = Util.findEntity(key);
		return entity;
	}

	/**
	 * Gets the single follow.
	 * 
	 * @param userId
	 *            the user id
	 * @param followId
	 *            the follow id
	 * @return the single follow
	 */
	public static Entity getSingleFollow(final long userId, final long followId) {
		Key parentKey = KeyFactory.createKey(AppConstant.ENTITY_USER, userId);
		Key key = KeyFactory.createKey(parentKey, AppConstant.ENTITY_FOLLOW,
				followId);
		Entity entity = Util.findEntity(key);
		return entity;
	}

	/**
	 * Gets the all follows.
	 * 
	 * @return the all follows
	 */
	public static Iterable<Entity> getAllFollows() {
		Iterable<Entity> entities = Util.listEntities(
				AppConstant.ENTITY_FOLLOW, null, null);
		return entities;
	}

	
	/**
	 * Gets the follows.
	 *
	 * @param userId the user id
	 * @param offset the offset
	 * @return the follows
	 */
	public static List<Entity> getFollows(final long userId, final int offset) {
		Entity user = User.getUser(userId);
		if (user != null) {

			Query query = new Query(AppConstant.ENTITY_FOLLOW);
			query.setAncestor(user.getKey());
			query.setFilter(new FilterPredicate(Entity.KEY_RESERVED_PROPERTY,
					FilterOperator.GREATER_THAN, user.getKey()));
			query.setFilter(new FilterPredicate(STATUS_FIELD,
					FilterOperator.EQUAL, AppConstant.CONFIRMED_STATUS));
			List<Entity> entities = Util.listChildren(query, offset);
			List<Entity> users = new ArrayList<>();
			users.add(user);
			for (Entity entity : entities) {
				Entity userEntity = User.getUser((long) entity
						.getProperty(FOLLOW_ID_FIELD));
				users.add(userEntity);
			}
			return users;
		}
		return null;
	}

	/**
	 * Gets the requests.
	 * 
	 * @param userId
	 *            the user id
	 * @param offset
	 *            the offset
	 * @return the requests
	 */
	public static List<Entity> getRequests(final long userId, final int offset) {
		Entity user = User.getUser(userId);
		if (user != null) {
			Query query = new Query(AppConstant.ENTITY_FOLLOW);
			//Use CompositeFilter to combine multiple filters
			Filter filter  =
			  CompositeFilterOperator.and(new FilterPredicate(FOLLOW_ID_FIELD,
						FilterOperator.EQUAL, userId), new FilterPredicate(STATUS_FIELD,
								FilterOperator.EQUAL, AppConstant.REQUEST_STATUS));
			
			query.setFilter(filter);

			Iterable<Entity> entities = Util.listChildren(query, offset);
			List<Entity> users = new ArrayList<>();
			for (Entity entity : entities) {
				Entity userEntity = User.getUser((long) entity
						.getProperty(USER_ID_FIELD));
				users.add(userEntity);
			}
			return users;
		}
		return null;
	}

	/**
	 * Gets the followed.
	 * 
	 * @param userId
	 *            the user id
	 * @param offset
	 *            the offset
	 * @return the followed
	 */
	public static List<Entity> getFollowedList(final long userId,
			final int offset) {
		Entity user = User.getUser(userId);
		if (user != null) {
			Query query = new Query(AppConstant.ENTITY_FOLLOW);
//			query.setFilter(new FilterPredicate(Entity.KEY_RESERVED_PROPERTY,
//					FilterOperator.EQUAL, userId));
//			query.setFilter(new FilterPredicate(STATUS_FIELD,
//					FilterOperator.EQUAL, AppConstant.CONFIRMED_STATUS));
			
			//Use CompositeFilter to combine multiple filters
			Filter filter  =
			  CompositeFilterOperator.and(new FilterPredicate(FOLLOW_ID_FIELD,
						FilterOperator.EQUAL, userId), new FilterPredicate(STATUS_FIELD,
								FilterOperator.EQUAL, AppConstant.CONFIRMED_STATUS));
			
			query.setFilter(filter);
			Iterable<Entity> entities = Util.listChildren(query, offset);
			List<Entity> list = new ArrayList<>();
			// list.add(user);
			for (Entity entity : entities) {
				Entity userEntity = User.getUser((long) entity
						.getProperty(USER_ID_FIELD));
				list.add(userEntity);
			}
			return list;
		}
		return null;
	}

	/**
	 * Gets the follow count.
	 * 
	 * @param userId
	 *            the user id
	 * @return the follow count
	 */
	public static int getFollowCount(final long userId) {
		Entity user = User.getUser(userId);
		if (user != null) {
			Iterable<Entity> entities = Util.listChildren(
					AppConstant.ENTITY_FOLLOW, user.getKey());
			int count = 0;
			for (@SuppressWarnings("unused")
			Entity entity : entities) {
				count++;
			}
			return count;
		}
		return 0;
	}

	/**
	 * Find follow by follow id.
	 * 
	 * @param followId
	 *            the follow id
	 * @return the iterable
	 */
	public static Iterable<Entity> findFollowByFollowId(final long followId) {
		return Util.listEntities(AppConstant.ENTITY_FOLLOW, FOLLOW_ID_FIELD,
				Long.toString(followId));
	}

	/**
	 * Delete Follow.
	 * 
	 * @param userId
	 *            the user id
	 * @param followId
	 *            the follow id
	 * @return the string
	 */
	public static String deleteFollow(final long userId, final long followId) {
		Entity entity = getSingleFollow(userId, followId);
		if (entity != null) {
			Util.deleteEntity(entity.getKey());
			return ("Follow deleted successfully.");
		} else {
			return ("Follow not found");
		}
	}

	/**
	 * Delete all follows.
	 */
	public static void deleteAllFollows() {
		Iterable<Entity> entities = getAllFollows();
		for (Entity entity : entities) {
			deleteFollow(Long.parseLong(entity.getProperty(USER_ID_FIELD)
					.toString()), Long.parseLong(entity.getProperty(
					FOLLOW_ID_FIELD).toString()));
		}
	}

}
